Принципы объектно-ориентированного программирования

         

Web-службы

Распределение функций приложений и возможность использования данных за пределами предприятия, на котором они хранятся, — следующий шаг в развитии компонентной технологии. Количество служб, которые разработчики могут интегрировать в свои приложения, настолько велико, что не приходится даже мечтать о том, чтобы создать их все самостоятельно. Рассмотренный нами конкретный пример, — система бронирования Acme (Acme Reservation System), — представляет собой простую службу. Бюро путешествий Acme (Acme Travel Agency), в состав которого входит система бронирования билетов на авиарейсы и система бронирования мест в гостинице, предоставляет клиентам более широкий спектр услуг.
Отдельному производителю не по силам создать для технологии распределенной обработки данных соответствующую инфраструктуру. Это означает, что и дальше будут сосуществовать, как минимум, миры Java, .NET, переносных компьютеров (ноутбуков) и традиционных (унаследованных) систем. К счастью, протоколы TCP/IP (Transmission Control Protocol/Internet Protocol — протокол управления передачей/протокол Internet) и HTTP (Hypertext Transfer Protocol — протокол передачи гипертекстовых файлов) приобрели статус сетевых стандартов. Они могут послужить базисом при попытке организовать взаимодействие неоднородных (гетерогенных) систем. Поскольку протокол передачи гипертекстовых файлов HTTP — протокол текстовый, то имеет смысл описывать взаимодействие таких систем на языке XML (extensible Markup Language — расширяемый язык разметки), который имеет статус промышленного стандарта. В Web-службах протоколы, основанные на языке XML и протоколе передачи гипертекстовых файлов HTTP, используются в качестве промышленных стандартов, которые обеспечивают взаимодействие разнообразных систем.
Web-службы — вторая часть истории распределенной обработки данных (распределенных вычислений) на платформе .NET. Если все приложения и службы, которые должны взаимодействовать, используют общеязыковую среду выполнения CLR (Common Language Runtime), то для организации распределенной обработки данных можно воспользоваться средствами дистанционной обработки, предусмотренными в платформе .NET. Преимущество средств дистанционной обработки, предусмотренных в платформе .NET, состоит в том, что с помощью удаленной сериализации (преобразования в последовательную


форму) объектов, можно получить доступ практически к любой структуре данных .NET. В то же время среда, которая не поддерживает платформу .NET, не позволяет обрабатывать все типы данных, имеющиеся в .NET. Следовательно, набор структур данных, передаваемых Web-службами, значительно уже, чем тот, который может быть описан с помощью протоколов, основанных на языке XML. Именно поэтому в Web-службах часто используются протоколы, основанные на языке XML. Поэтому также в версии протокола SOAP (Simple Object Access Protocol — простой протокол доступа к объектам), которая используются Web-службами, и в версии, на базе которой организована удаленная обработка данных в .NET, предусмотрены разные модели программирования. Так, протокол SOAP, используемый в .NET, полностью совместим с общеязыковой средой выполнения CLR (Common Language Runtime). На версию протокола SOAP, посредством которого взаимодействуют Web-службы, накладывают некоторые ограничения стандарты, определяющие способность к взаимодействию.
Как уже было сказано, Web-службы позволяют соединить неоднородные (гетерогенные) системы. Кроме этого, Web-службы позволяют деловым партнерам совместно использовать информацию. С помощью Web-служб можно объединить унаследованные (традиционные) системы с используемыми, не разрабатывая для их соединения специализированного промежуточного программного обеспечения. Использование Web-служб может оказаться полезным даже в пределах одного предприятия, поскольку с их помощью можно объединить информацию, полученную от внутренних и внешних источников. Однако для того, чтобы превратить Web-службы в нечто большее, чем средство разработки распределенных приложений, нужно разработать финансовую и правовую инфраструктуру, а также инфраструктуру, обеспечивающую надежность и безопасность передаваемых данных.

Протоколы

В основе Web-служб используется несколько протоколов: язык XML (extensible Markup Language), пространства имен XML (XML Namespaces), Схема XML (XML Schema), SOAP (Simple Object Access Protocol, простой протокол доступа к объектам) и WSDL (Web Services Description Language, язык описания Web-служб). Некоторые из этих протоколов утверждены Консорциумом W3C в качестве промышленных стандартов. Некоторые же, например WSDL, лишь приобретают широкое распространение и в качестве стандарта еще не признаны.



Язык XML

Язык XML, который принят Консорциумом W3C в качестве промышленного стандарта [Формально, окончательные версии документов, разработанных Консорциумом W3C, называют рекомендациями. Мы же будем называть их стандартами или спецификациями. Документы, разрабатываемые Консорциумом W3C, которые еще не приобрели статус рекомендации, называют именами, присвоенными им Консорциумом W3C: proposed recommendation (предложенная рекомендация), candidate recommendation (претендент на статус рекомендации), last-call working draft (окончательная версия рабочего черновика), working draft (рабочий черновик) и notes (записки). ], описывает структуру документа путем указания связей между базовыми элементами документа. Элементы документа могут содержать описательную информацию, называемую атрибутами. Сами элементы могут содержать другие элементы, т.е. иметь сложную структуру. Поскольку такие документы можно записать в текстовом виде [Но не обязательно в виде текста. Можно построить приложение, используя абстракции, описанные в разработанном Консорциумом W3C документе Information Set (Информационный набор, класс информации, информационное множество, сокращенно Infoset), который имеет статус претендента на рекомендацию (proposed recommendation). Используя такие абстракции, как документ, пространство имен, элемент, символ и атрибут, можно описать иерархию XML-документа, не привязываясь к конкретному формату, в котором хранятся XML-данные. Например, в решениях, предназначенных для переносных систем, естественно использовать более экономный двоичный формат XML-документов, а не представлять их в виде текста. Рекомендация XML Schema (схема XML), была разработана на основе Infoset, и потому в ней не используются синтаксические конструкции с угловыми скобками. Для применения Информационного набора (Information Set) необходимо пространство имен XML (XML Namespeces).], то на языке XML их можно представить в таком виде, который не зависит от используемой платформы и пригоден для передачи по сети. Так как порт 80, используемый протоколом передачи гипертекстовых файлов HTTP, всегда открыт, представленные в текстовом виде данные беспрепятственно проходят через брандмауэры (аппаратно-программные средства сетевой защиты). Ниже приведено описание списка CustomerList (СписокКлиентов), который состоит из нескольких клиентов, в формате XML.

<CustomerList> <Customer>
<FirstName>John</FirstName>
<LastName>Smith</LastName>
<EmailAddress>smith@smith.org</EmailAddress> </Customer> <Customer>
<FirstName>Sally</FirstName>
<LastName>Rutherford</LastName>
<EmailAddress>srutherford@cando.com</EmailAddress> </Customer> </CustomerList>



Пространства имен XML (XML Namespeces)

Множество элементов и атрибутов, которые содержатся в ХМL-документе, называется словарем. Словарь может оказаться очень полезным, если он моделирует многократно используемую информацию. Например, можно создать финансовый или химический словарь. Наличие пространства имен позволяет предотвратить возможные конфликты, так как словарям могут быть присвоены уникальные имена. А благодаря этому словари можно использовать в разных приложениях.
Ниже приведен пример ХМL-документа. Для того чтобы отличить элементы <FirstName> (<Имя>), <LastName> (<Фамилия>) и <EmailAddress> от любых других определений, которые могут описываться тем же тэгом, но при этом иметь другое значение или употребляться в другом контексте, используется атрибут пространства имен, которому принадлежит элемент. Из данного примера можно видеть, что допускается сокращение названия пространства имен. С практической точки зрения очень удобно использовать в документе составные пространства имен.

<CustomerList xmlns:cl= "urn:uuid 28833F1C-CBE4-4042-9B35-BF641DFB35DC">
<cl:FirstName>John</cl:FirstName>
<cl:LastName>Smith</cl:LastName>
<cl:EmailAddress>smith@smith.org</cl:EmailAddress> </CustomerList>

Пространство имен XML идентифицируется с помощью универсального идентификатора ресурса (Uniform Resource Identifier— URI). Универсальным идентификатором ресурса (URI) может выступать унифицированный указатель информационного ресурса (Uniform Resource Locator— URL) или унифицированное имя ресурса (Uniform Resource Name — URN). Они оба определяют уникальное имя. С унифицированными указателями информационного ресурса (URL), которые, по сути, являются адресами Web-узлов, мы уже знакомы. Они уникальны, поскольку присваиваются централизованно ведомством, ответственным за назначение имен. Унифицированное имя ресурса, URN, — это всего лишь уникальная строка. Можно использовать URN, определенное с помощью глобально уникального идентификатора (GUID) [Глобально уникальный идентификатор (Globally Unique Identifier — GUID), представляет собой 128-битовый идентификатор, уникальность которого гарантируется. Глобально уникальные идентификаторы (GUID) широко используются в модели компонентных объектов Microsoft (COM). Свой собственный глобально уникальный идентификатор (GUID) вы можете сгенерировать при помощи сервисной программы guidgen.exe (имеет Windows-интерфейс) или uuidgen. exe (имеет интерфейс командной строки). Эти сервисные программы расположены в каталоге ...\Microsoft Visual Studio.NET\Common7\Tools. ], например urn:uuid:28833F!C-CBE4-4042-9B35-BF641DFB35DC [В данном примере глобально уникальные идентификаторы (GUID) используются из соображений простоты, а также для того, чтобы гарантировать уникальность, а не просто существование самого идентификатора. В реальных системах используются имена на основе унифицированных указателей информационных ресурсов (URL), независимо от того, существуют ли эти унифицированные указатели информационных ресурсов (URL) на самом деле. ]. Универсальный идентификатор ресурса (URI), который идентифицирует пространство имен, не обязан совпадать с идентификатором, обозначающим какой-либо Web-pecypc.



Схема XML (XML schema)

Пространства имен языка XML не приписывают никакой семантики используемым данным. В спецификации XSD (XML Schema Definition — Определение схемы XML) определяется набор базовых типов данных и их смысл, на основе чего можно определять новые типы данных. Другими словами, схема XML присваивает определенный смысл структуре документа. Сама схема описывается средствами языка XML. Приведенный раньше документ CustomerList можно описать следующей схемой:

<schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:cl="http://www.acme.com/CustomerList"
targetNamespace="http://www.acme.com/CustomerList">
<xsd:complexType name="Customer"> <xsd:sequence>
<xsd:element name="FirstName" type="xsd:string" />
<xsd:element name="LastName" type="xsd:string" />
<xsd:element name="EmailAddress" type="xsd:string"/>
</xsd:sequence>
</xsd:complexType>
</schema>

Элемент targetNamespace обозначает имя схемы. В качестве имени используется определенный спецификацией XSD (XML Schema Definition— Определение схемы XML) элемент "string" ("строка"). Спецификация XSD позволяет ограничить диапазон принимаемых значений, указать количество вхождений экземпляра и приписать элементам атрибуты. Сама схема создается средствами языка XML. Как документ, так и связанная с ним схема проверяются на правильность и обрабатываются как обычные XML-документы. Один и тот же документ, интерпретируемый двумя разными схемами, имеет два разных смысла.



Протокол SOAP

С помощью схемы XML можно определить типы используемых данных. Но, кроме этого, необходимо принять еще ряд соглашений о способе передачи данных и их типов по сети. С этой целью протокол SOAP (Simple Object Access Protocol — простой протокол доступа к объектам), использует язык XML. (Иными словами, язык XML сам выступает в качестве протокола.)
Тип данных, передаваемых по протоколу SOAP, определяется с помощью схемы XML. Но протокол SOAP разработан гораздо раньше, чем была завершена работа над спецификацией Схема XML (XML Schema). Поэтому эти две спецификации несколько отличаются. Отличие состоит в следующем. Схема XML описывает иерархическую, или древовидную структуру. Протокол SOAP предназначен для представления объектов, а объекты могут иметь куда более сложные отношения, чем иерархические. Классы, например, могут иметь несколько родительских классов. Как мы увидим дальше, данное отличие проявилось также и в усложнении Web-служб. В данный момент Консорциум W3C пытается согласовать протокол SOAP со Схемой XML (XML Schema).
Протокол SOAP версии 1.1 для передачи данных может использовать не только протокол передачи гипертекстовых файлов HTTP, но и несколько других транспортных протоколов.
Протокол SOAP используется Web-службами различных платформ Microsoft, а не "только в .NET. Компания Microsoft выпустила комплект инструментальных средств SOAP Toolkit (Инструментарий SOAP), с помощью которого можно разрабатывать Web-службы на базе любой Windows-платформы. Но поддержка SOAP встроена в .NET. В состав SOAP Toolkit (Инструментарий SOAP) входит сервисная программа SOAP Trace Utility (Утилита трассировки SOAP), которая особенно полезна при отслеживании обычных и форматированных сообщений, передаваемых по протоколу SOAP.



Язык описания Web-служб WSDL

Объекты содержат описание своего состояния и поведения. Схемы описывают данные. Язык описания Web-служб WSDL (Web Services Description Language) описывает Выходящие в состав Web-службы методы и данные. Вскоре мы рассмотрим простой при-||лер, с помощью которого проиллюстрируем, как создать Web-службу, не используя язык PVSDL. Но если вы хотите, чтобы автоматически создавались классы, вызывающие Web-ьслужбу или Web-службы выполняли действия, которые могут потребовать автоматического вмешательства машины, тогда без языка WSDL не обойтись [Это напоминает библиотеку типов среды Visual Basic 6, которая упрощает программирование в модели Компонентных объектов Microsoft COM. В отличие от библиотеки типов, которая описывает не все rCOM-объекты и интерфейсы, язык WSDL обеспечивает исчерпывающее описание Web-служб. ]. Ибо в противном Ьсяучае составлять и отсылать SOAP-сообщения вам придется вручную. Р Как вы узнаете из следующего примера, протокол SOAP, который описывает формат пересылаемых Web-службами данных, определен в языке WSDL. В данный момент документ Консорциума W3C, описывающий язык WSDL, имеет статус записки (note).



Архитектура Web-службы

Информационный сервер Internet компании Microsoft (Internet Information Server — HIS) взаимодействует как со страницами ASP.NET так и с Web-службами, которые отсыпают ему запросы по протоколу передачи гипертекстовых файлов HTTP. Запросы кодируются как часть унифицированного указателя информационного ресурса (URL) или представляются в виде XML-текста. В ответ на запрос Web-службы информационный сервер Internet (US) создает запрошенный объект. Затем информационный сервер Internet (US) вызывает метод объекта, который обрабатывает соответствующий запрос. Любые возвращаемые данные преобразуются в XML-формат и возвращаются клиенту как ответ по протоколу передачи гипертекстовых файлов HTTP.



Пример Web-службы Add (Сложение)

Чтобы ознакомиться с архитектурой Web-служб, построенных на базе платформы Microsoft .NET, и протоколами, которые они используют, мы рассмотрим простую Web-службу. Она расположена в каталоге SimpleWebService. Web-служба Add (Сложение) складывает два числа. С целью облегчить восприятие материала, мы создадим Web-службу наиболее простым способом и не будем использовать шаблон проекта Web-службы на управляемом C++ (Managed C++ Web Service project template). Максимально упрощая реализацию данной службы, мы воспользуемся шаблоном приложения на основе библиотеки классов управляемого C++ (Managed C++ Class Library). Чтобы запустить пример SimpleWebService на выполнение, следуйте пошаговым инструкциям:
Откройте проект SimpleWebService\WebService\WebService. sin в среде Visual Studio.NET. Постройте решение.
Скопируйте полученный в результате этого файл WebService.dll в каталог Simple-WebService\WebService\bin. Несмотря на то, что все эти действия уже сделаны за вас, желательно, чтобы вы проделали их самостоятельно.
В окне проводника Windows (Windows Explorer) щелкните правой кнопкой мыши на каталоге SimpleWebService\WebService и выберите из контекстного меню команду Properties (Свойства). В окне Properties (Свойства) перейдите на вкладку Web Sharing (Совместное использование в Web). Откройте папку для совместного доступа, указав в качестве псевдоимени SimpleWebService. Чтобы закрыть окно Properties (Свойства), щелкните на ОК.
Просмотрите пример, для чего введите следующий унифицированный указатель информационного ресурса (URL) Http://localhost/Simple\VebService/Add.asmx в адресной строке вашего броузера. Его также можно открыть в среде Visual Studio.NET стандартным способом. Данный проект сконфигурирован так, что при этом запустится Internet Explorer (IE).
Дальше необходимо написать код и сохранить его в файле с расширением asmx в подкаталоге или виртуальном подкаталоге корневого каталога информационного сервера Internet (US). Таким образом мы создали простую Web-службу [По умолчанию корневым каталогом является каталог \metpub\wwwroot. ]. Информационный сервер Internet (US) использует понятие виртуального каталога. Это означает, что каталог, в котором фактически расположены файлы, необходимые информационному серверу Internet (IIS), не обязательно должен на самом деле находиться в корневом каталоге информационного сервера Internet (IIS). Проще всего создать виртуальный каталог следующим образом. Выделите нужный каталог в окне Проводника NT (NT Explorer). Щелкните правой кнопкой мыши на каталоге и выберите из контекстного меню команду Sharing (Совместное использование). Перейдите на вкладку WebSharing (Совместное использование в Web) и создайте на базе данного каталога виртуальный каталог информационного сервера Internet (US).
Файл Add.asmx содержит ссылку на класс Test (Тест), с помощью которого реализована Web-служба. Этот файл нужно разместить в каталоге SimpleWebService.

<%@ WebService Class=Test %>

Файл WebService. h содержит класс Test (Тест), который является производным от класса WebService, принадлежащего пространству имен System: :Web: :Services (Система::Сеть::Службы). Метод, содержащийся в этом классе, может использоваться в качестве метода Web-службы, если он имеет атрибут WebMethod, как показано на примере метода Add (Сложение), реализованного в файле WebService. h.

// WebService.h
fusing <mscorlib.dll>
fusing <System.dll>
fusing <System.Web.dll>
fusing <Systern.EnterpriseServices.dll>
fusing <System.Web.Services.dll>
using namespace System;
// использование пространства имен Система;
using namespace System::Web;
// использование пространства имен Система::Сеть;
using namespace System::Web::Services;
// использование пространства имен Система::Сеть::Службы;
public _gc class Test : public WebService
{
public:
[WebMethod]
long Add(long x, long y) {
return x + y; }
};

Этот файл находится в подкаталоге WebService каталога SimpleWebService данной главы. Создайте на базе каталога WebService виртуальный каталог с псевдоименем (псевдонимом) SimpleWebService [Как указывается в главе 10 "ASP NET и Web-формы", проще всего создать виртуальный каталог с помощью Проводника Windows (Windows Explorer). Щелкните правой кнопкой мыши на нужном каталоге и из контекстного меню выберите команду Properties (Свойства). На вкладке Web Sharing (Совместный доступ в Web), вы можете открыть папку для совместного доступа и присвоить ей псевдоимя (псевдоним). ]. После построения службы Add (Сложение) в виртуальном каталоге WebService создайте каталог bin и скопируйте в него сборку WebService.dll. Информационный сервер Internet (US) автоматически ищет требуемые файлы в этом каталоге.



Просмотр Web-службы Add (Сложение) при помощи броузера

Internet Explorer можно применять в качестве простой клиентской программы, которая использует возможность кодирования запросов к Web-службе в унифицированном указателе информационного ресурса (URL) для метода GET (ПОЛУЧИТЬ) протокола передачи гипертекстовых файлов HTTP Результат просмотра унифицированного указателя информационного ресурса (URL) http://localhost/SimpleWebService/Add.asmx в окне Internet Explorer показан на рис. 11.1



Рис. 11.1 Вызов Web-службы Add (Сложение) с помощью Internet Explorer



Отладка Web-службы Add (Сложение)

Проекты Web-служб отлаживаются аналогично ASP NET-примерам, рассмотренным в предыдущей главе Сначала щелкните правой кнопкой мыши на проекте Web-службы в окне решения (Solution Explorer) и выберите из контекстного меню команду Properties (Свойства). Затем в окне Project Property Page (Страница свойств проекта) выберите команду Configuration Properties (Свойства конфигурации) Теперь выберите команду Debugging (Отладка) и внесите указанные изменения [По вашему усмотрению, вы можете использовать другую программу-клиент ].

В строке Command (Команда) введите aspnet_wp. ехе В строке Attach (Прикрепить) введите Yes (Да) В строке HTTP URL (унифицированный указатель информационного ресурса (URL) для протокола передачи гипертекстовых файлов HTTP) введите

http://localhost/SimpleWebService/Add.asmx.

Кроме этого, необходим конфигурационный файл web.config, в котором нужно включить режим отладки Этот файл следует разместить в том же каталоге, в котором находится Web-служба Ниже приведен пример конфигурационного файла

<?xml version="l.О" encoding="utf-8" ?> <configuration>
<system.web>
<compilation debug="true"/>
</system.web> </configuration>

После того как выполните указанные действия, вы сможете отлаживать проекты Web-служб, используя стандартный отладчик среды Visual Studio.NET. Каждый раз после внесения изменений в проект или нового построения проекта не забывайте копировать динамически подключаемую библиотеку (DLL), в которой содержится Web-служба, из каталога проекта в виртуальный каталог Web-службы. Если этого не сделать, информационный сервер Internet (US) будет использовать старую версию Web-службы.



Клиент для Web-службы Add (Сложение)

Безусловно, у вас возникнет желание написать клиентские программы, которые входят в состав распределенного приложения и используют Web-службу. Вскоре мы это сделаем. Пока же в качестве клиента мы будем использовать Internet Explorer. С его помощью мы изучим протокол, который используется для обмена данными с Web-службой. Чтобы начать, введите в адресной строке Internet Explorer следующий унифицированный указатель информационного ресурса (URL):

http: //localhost/SimpleWebService/Add.asmx.

Если вы щелкните на ссылке Add (Сложение), то будет выведена форма, заполнив которую, вы сможете отослать запрос Web-службе Add (Сложение). Вслед за формой, на этой же странице, будет приведено описание различных протоколов, которые протокол передачи гипертекстовых файлов HTTP использует для того, чтобы отослать запрос. В наших целях заслуживают внимания два протокола- HTTP GET и SOAP.
Мы рассмотрим протокол HTTP GET, поскольку именно он используется формой, отображаемой в окне Internet Explorer. В этом протоколе, метки-заполнители, которые заменяются фактическими данными, выделены жирным шрифтом:

GET /SimpleWebService/Add.asmx/Add?x=string&y=string HTTP/1.1 1

Введенные в форму данные кодируются как часть унифицированного указателя информационного ресурса (URL). (Это стандартный способ выполнения запроса с помощью метода GET протокола передачи гипертекстовых файлов HTTP.) Возвращаемые данные имеют следующий формат:

НТТР/1.1 200 ОК
Content-Type: text/xml; charset=utf-8
Content-Length: length
<?xml version="l.0" encoding="utf-8"?> <int xmlns="http://tempuri.org/">int</int>

На рис. 11.2 показана заполненная форма с введенными значениями слагаемых Если теперь щелкнуть на кнопке Invoke (Активизировать), будет вызвана соответствующая Web-служба.
Затем появится окно Internet Explorer, в котором будет отображена часть данных ответа, сгенерированного Web-службой и передаваемого ею по протоколу передачи гипертекстовых файлов HTTP Фактическое возвращаемое значение (результат) содержится среди этих данных

<?xml version="1.0" encoding="utf-8" ?>
<int xmlns="http://tempuri.org/">9</int>

Возвращаемые данные имеют формат, определенный в описании протокола, однако вместо метки-заместителя подставлен ответ (9). Следует обратить внимание, что по протоколу HTTP GET могут передаваться лишь простые типы данных.



Рис 11.2 В окне Internet Explorer отображается форма, в которую уже введены данные

Более интересным является протокол SOAP Он кодирует запросы и ответы с помощью метода POST протокола передачи гипертекстовых файлов HTTP Данные, которые фактически передаются в запросе, подставляются вместо меток-заполнителей В листинге метки-заполнители выделены жирным шрифтом
Сначала мы рассмотрим формат запросов протокола SOAP, передаваемых с помощью метода POST протокола передачи гипертекстовых файлов HTTP Первая часть такого запроса содержит набор заголовков, относящийся к протоколу передачи гипертекстовых файлов HTTP В информационной части (в разделе данных) запроса (теле объекта), передаваемого по протоколу передачи гипертекстовых файлов HTTP, содержатся данные Для описания этих данных протокол SOAP использует язык XML Описание данных всегда отделяется от заголовков пустой строкой Заголовок content-length (длина содержимого) определяет длину данных, которая зависит от размера параметров в информационной части (в разделе данных) запроса
Заголовок method (метод) указывает файл, которому будет направлен запрос Здесь также можно указать имя объекта, который будет обрабатывать запрос (конечную точку) В заголовке SOAPAction (SOAP-Действие) указывается имя метода, который вызывается Web-службой, и пространство имен, которому он принадлежит [Читатели, знакомые с моделью компонентных объектов Microsoft (COM), увидят аналогию между описанием методов с помощью пространства имен и определением глобально уникального идентификатора интерфейса (Interface Identifier— IID) с помощью глобально уникального идентификатора (Globally Unique Identifier — GUID) ].
Для описания параметров метода в протоколе SOAP используется язык XML [Аналогом языка описания интерфейсов (Interface Definition Language — IDL), используемого в модели компонентных объектов Microsoft (COM), является язык описания Web-служб (Web Services Description Language — WSDL), который будет рассмотрен несколько позже Протокол SOAP является аналогом сетевого формата NDR (Network Data Representation), который используется в распределенной модели компонентных объектов DCOM (Distributed Component Object Model) Все параллели, которые можно провести между технологией Web стужб и моделью компонентных объектов Microsoft (COM), описаны в статье Дона Бокса (Don Box) A Young Person's Guide to the Simple Object Access Protocol (Руководство для молодого человека по Простому протоколу доступа к объектам) которая появилась в 2000 году в мартовском выпуске собрания документов компании Microsoft MSDN (Microsoft Developer Network)]. В информационной части (теле) SOAP-запроса содержатся параметры, передаваемые вызываемому методу При реальном вызове метода вместо меток-заполнителей int будут подставлены фактические параметры, которые нужно передать методу Web-службы

POST /SimpleWebService/Add.asmx HTTP/1.1
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http.//tempuri.org/Add"
<">xml version="l 0" encoding="utf-8'"5>
<soap:Envelope xmlns xsi="http://www.w3 org/2001/XMLSchemainstance"
xmlns:xsd="http.//www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Add xmlns="http //tempun.org/"> <x>int</x> <y>int</y> </Add>
</soap.Body> </soap:Envelope>

Ниже мы опишем ответ, передаваемый по протоколу SOAP с помощью метода POST протокола передачи гипертекстовых файлов HTTP В ответе метка-заполнитель int замещается фактическим возвращаемым значением

НТТР/1.1 200 ОК
Content-Type: text/xml; charset=utf-8
Content-Length, length
<?xml version="1.0" encoding="utf-8'">>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchemainstance"
xmlns:xsd="http.//www.w3 org/2001/XMLSchema"
xmlns soap="http //schemas.xmlsoap org/soap/envelope/">
<soap.Body>
<AddResponse xmlns="http //tempuri org/">
<AddResult>int</AddResult>
</AddResponse>
</soap.Body>
</soap:Envelope>



Язык описания Web-служб (Web Services Description Language — WSDL)

Протокол SOAP описывает передаваемые данные Интерфейс Web-служб он не описывает Данные, передаваемые по протоколу SOAP, можно закодировать и самостоятельно Но лучше для этой цели создать вспомогательные классы (классы-заместители), которые будет использовать программа-клиент Ведь чтобы самостоятельно закодировать данные, необхсцшо знать спецификацию SOAP до мельчайших подробностей и провести синтаксичесий разбор возвращаемого XML-пакета.
Язык WSDl(Web Services Description Language — язык описания Web-служб) предназначен для опиания интерфейса Web-служб. В качестве примера мы опишем с помощью языка WSDL нтерфейс Web-службы SimpleWebService, имеющей один метод Add (Сложение). Ипользовать язык WSDL для описания интерфейса Web-служб, которые для передачи днных используют другие протоколы, а не протокол SOAP, мы не будем. Чтобы увидетюписание на языке WSDL Web-службы SimpleWebService, введите в адресной строе броузера следующий унифицированный указатель информационного ресурса (URL)http: //localhost/SimpleWebService/Add.asmx?WSDL. Описание Web-службы в языке WSDL состоит из нескольких разделов. В разделе <types> (<типы>) опредлены следующие типы:

Add (Сложение). Описывает данные, передаваемые Web-службе по протоколу SOAP; Addresponse. Описывает результаты, возвращаемые Web-службой клиенту по протжолу SOAP.

Тип Add (Сложение) имеет два элемента, каждый из которых встречается лишь один раз. Элементьимеют имена х и у и принадлежат типу long, определенному спецификацией XSD (XMLSchema Definition — Определение схемы XML). Возвращаемый параметр AddResponsecoдepжит один элемент AddResult, который встречается один раз и также принадлеэмт типу long, определенному, как мы уже знаем, в спецификации XSD (XML SchemaDefinition — Определение схемы XML). Обратите внимание, как рассмотренные типы >ке использовались ранее, при описании протокола SOAP.

<types>
<s:elemerz name="Add"> <s: comjl.exType> <s: sequence>
<salement minOccurs="1" maxOccurs="1"
name="x" type="s:int" />
<s:element minOccurs="1" maxOccurs="1"
name="y" type="s:int" />
</s:sequence>
</s:complexType>
</s:element>
<s:element name="AddResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="1" maxOccurs="1"
name="AddResult" type="s:int" />
</s:sequence>
</s:complexType>
</s :elment>
</types>

В разделе :message> (<сообщение>) описывается использование этих типов в качестве параметре.

<message name="AddSoapIn"> <!-- сообщение -->
<part name="parameters" element="s0:Add" /> <!-- параметры -->
</message> <!-- сообщение -->
<message name="AddSoapOut"> <!-- сообщение -->
<part name="parameters" element="s0:AddResponse" />
<!-- параметры -->
</message> <!-- сообщение -->

В разделе <portType> (тип порта) с Web-службой сопоставляются отдельные Web-методы, которые описаны в элементах <operation> (<действие>). Если бы данная Web-служба использовала большее количество методов, тогда в разделе <portType> (тип порта) было бы больше элементов operation (действие) [Читатели, склонные проводить параллели, увидят здесь аналогию с интерфейсом модели компонентных объектов Microsoft (COM). ]. Операции ввода и вывода каждого метода связаны с соответствующими сообщениями, которые были определены раньше.

<portType name="TestSoap">
<operation name="Add"> <!-- операция -->
<input message="s0:AddSoap!n" />
<output message="s0:AddSoapOut" />
</operation> <!-- операция -->
</portType>

В разделе <binding> (<связывание>) каждому действию ставится в соответствие способ кодирования и протокол передачи данных.

<binding name="TestSoap" type="s0:TestSoap">
<soap:binding
transport="http://sohemas.xmlsoap.org/soap/http"
style="dooument" /> <!-- стиль = "документ" -->
<operation name="Add"> <!-- название операции -->
<soap:operation soapAction="http://tempuri.org/Add" style="document" />
<!-- стиль = "документ -->
<input> <!-- ввод -->
<soap:body use="literal" />
</input> <!-- ввод -->
<output> <!-- вывод -->
<soap:body use="literal" />
</output> <!-- вывод -->
</operation> <!-- операция -->
</binding>

В разделе <service> (<служба>) указывается порт, используемый Web-службой, и адрес, по которому можно активизировать Web-службу.

<service name="Test"> <!-- имя = "Испытание -->
<
port name="ТезtSoap" binding="s0:TestSoap">
<!-- порт -->
<soap:address location=
"http://localhost/SimpleWebService/Add.asmx" />
</port> <!-- порт -->
</service>



Классы-заместители

Сервисная программа Wsdl. exe считывает описание Web-службы на языке WSDL и генерирует класс-заместитель, который формирует SOAP-запрос Web-службы. По умолчанию эта сервисная программа генерирует код класса-заместителя на языке С# [Можно выбрать CS, VB, JS или (в случае доступности) указать реализацию класса System::CodeDom::Compiler::CodeDomProvider (Система::СоёеОот::Компилятор::Сос1еОогпРгоУ1с1ег) на любом языке. ]. Язык C++ в данный момент она не поддерживает. Следовательно, в нашем примере класс-заместитель будет создан на С#. По умолчанию сервисная программа Wsdl. exe использует протокол SOAP [В конфигурационном файле можно указать также протоколы HttpGet, HttpPost или другой специальный протокол.]. В результате выполнения команды, приведенной ниже, будет сгенерирован класс-заместитель addproxy. cs на языке С#.

wsdl /out:addproxy.cs
http://localhost/SimpleWebService/Add.asmx?WSDL

В файле addproxy. cs содержится исходный код класса-заместителя, в котором определен один конструктор и три метода. Конструктор устанавливает унифицированный указатель информационного ресурса (URL) данной Web-службы. Один из трех имеющихся методов реализует синхронный блокирующий вызов Web-службы. С помощью оставшихся двух методов реализован шаблон асинхронного вызова Web-службы, который обсуждался в главе 8 "Классы каркаса .NET Framework". Для асинхронного вызова Web-службы можно использовать методы BeginXXX и EndXXX созданного класса-заместителя [Очевидно, что в данном примере XXX = Add.]. Класс-заместитель имеет имя WebService, т.е. его имя совпадает с именем класса WebService.
Метод Invoke (Активизировать) класса SoapHttpClientProtocol формирует запросы, передаваемые по протоколу передачи гипертекстовых файлов HTTP, и обрабатывает ответы, получаемые по тому же протоколу. Сами же запросы и ответы содержатся в отсылаемых и получаемых SOAP-пакетах. Описанный выше пример класса-заместителя расположен в подкаталоге SimpleAddClient каталога SimpleWebService.

public class Test :
System.Web.Services.Protocols.SoapHttpClientProtocol
// общедоступный класс Испытание:
// Система.Сеть.Службы.Протоколы.SoapHttpClientProtocol
{
public Test ()
{
this.Url =http://localhost/SimpleWebService/Add.asmx";
}
public int Add(int x, int y)
{
object [] results = this.Invoke("Add", // результаты
new object[] {x, y}); return ((int)(results[0]));
}
public System.lAsyncResult BeginAddfint x, int y,
System.AsyncCallback callback, object asyncState)
{
return this.Beginlnvoke("Add",
new object[] {x, y}, callback, asyncState);
}
public int EndAdd(System.lAsyncResult asyncResult)
{
object[] results = this.Endlnvoke(asyncResult) ;
// результаты
return ( (int) (results [0]));
}
}

Немного раньше мы показали, как получить доступ к Web-службе с помощью броузера. Вызов Web-службы с помощью броузера может оказаться полезным на этапе тестирования приложения. Но, как правило, Web-служба взаимодействует со специально разработанной для этого клиентской программой. Можно написать клиентскую программу, которая будет отсылать запрос Web-службе, используя созданные нами классы-заместители. В каталоге SimpleWebService\SimpleAddClient расположен файл Main, cs с кодом клиентской программы. Клиент реализован на языке С#. Язык С# выбран только из соображений удобства, ведь ранее созданные нами классы-заместители также написаны на С#. Клиентская программа отсылает запрос Web-службе. Web-служба вычисляет результат сложения 1 и 2, и возвращает значение 3, которое отображается в окне, представляющем консоль клиентской программы.

public class AddClient
// общедоступный класс AddClient
{
public static void Main(string[] args)
{
Test 2 = new Test();
long f = z.Addd, 2) ;
Console.WriteLine(f) ;
return;
}
}

Смешивать в одном модуле языки С# и C++ нельзя. Если же вы пишите код клиентской программы на C++, его затем нужно скомпилировать и создать выполняемую сборку. Далее следует скомпилировать класс-заместитель, сгенерированный на С#, в результате чего будет получена отдельная сборка (динамически связываемая библиотека (DLL)). Таким образом, чтобы получить доступ к Web-службе, клиентская программа, написанная на C++, использует класс-заместитель, написанный на С#. Мы проиллюстрируем вышеизложенное позже в примере Arithmetic (Арифметика).



Клиент web-службы, использующий необработанные данные SOAP и протокол передачи гипертекстовых файлов HTTP

Чтобы проиллюстрировать возможности класса SoapHttpClientProtocol, окончательный вариант клиентской программы использует сокеты. Сокеты позволяют отсылать как заголовки протокола передачи гипертекстовых файлов HTTP, так и сами данные в формате SOAP, а также получать ответ от Web-служб. Клиентская программа расположена в подкаталоге RawAddClient каталога SimpleWebService.
Сначала главная (main) функция программы читает файл SoapAdd.txt. Этот файл содержит SOAP-заголовки вызываемой Web-службы. Данная функция возвращает длину содержимого, которое фигурирует в соответствующем заголовке запроса, формируемого методом POST протокола передачи гипертекстовых файлов HTTP.

long contentLength = 0;
StringBuilder *contentData =
BuildContent(
"SoapAdd.txt", ScontentLength);
StringBuilder *requestHeader =
BuildHeader(contentLength);

Затем клиентская программа устанавливает связь с сервером, отсылает ему данные и получает от сервера ответ, который выводится на консоль.

IPEndPoint *endPoint =
new IPEndPoint(
dynamic_cast<IPAddress *>
(Dns::Resolve(httpServer)->AddressList->
get_Item(0)),
httpPort);
Socket *sock =
new Socket(
AddressFamily::Internetwork,
SocketType::Stream,
ProtocolType::Tcp);
sock->Connect(endPoint);
...
sock->Send(
header, header->Length, SocketFlags::None);
// заголовок, заголовок-> Длина
sock->Send( // Посылает
content, content->Length, SocketFlags::None);
// содержание, содержание-> Длина
...
bytes = sock->Receive( // Получить
receivedData,
receivedData->Length, // Длина
SocketFlags::None);
Console::WriteLine(
ASCII->GetString(receivedData, 0, bytes));
sock->Close();
...

Функция BuildHeader с помощью метода POST протокола передачи гипертекстовых файлов HTTP формирует стандартный запрос, дополненный заголовком SOAPAction.

StringBuilder *sb = new StringBuilder(1024);
sb->Append( // Добавить в конец
"POST /SimpleWebService/Add.asmx HTTP/1.l\r\n") ;
sb->Append("Host: localhost\r\n"); // Добавить в конец
sb->Append( // Добавить в конец
"Content-Type: text/xml; charset=utf-8\r\n");
String *line = // Строка
String::Format("Content-Length: {0}\r\n", // Строка:: Формат
contentLength.ToString());
sb->Append(line); // Добавить в конец (строка)
sb->Append( // Добавить в конец
"SOAPAction: \"http://tempuri.org/Add\"\r\n") ;
sb->Append("\r\n");; // Добавить в конец
...

Функция BuildContent считывает в буфер содержимое файла и вычисляет размер буфера в байтах.

*contentLength = 0;
String *line; // Строка
while ((line = fileStream->ReadLine()) != 0)
{
sb->Append(line); // Добавляет в конец (строка)
sb->Append("\r\n"); // Добавляет в конец ("\r\n")
*contentLength += line->Length + 2;
// *contentLength + = строка-> Длина + 2;
}
fileStream->Close();

Данные в файле SoapAdd. txt представлены в SOAP-формате. Как и следовало ожидать, этот файл имеет вид, описанный выше. Входные параметры 9 и 3 представляются в соответствии со спецификацией WSDL.

<?xml version="l.О" encoding="utf-8"?>
<soap:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Add xmlns="http://tempuri.org/">
<x>9</x>
<y>3</y>
</Add>
</soap:Body>
</soap:Envelope>

Сначала программа формирует запрос. Для этого используется метод POST протокола передачи гипертекстовых файлов HTTP. Запрос содержит стандартные заголовки протокола передачи гипертекстовых файлов HTTP и дополнительный заголовок SOAPAction. Затем запрос кодируется согласно спецификации SOAP. Вывод программы приведен ниже:

POST /SimpleWebService/Add.asmx HTTP/1.1
Host: localhost
Content-Type: text/xml; charset=utf-8
Content-Length: 355
SOAPAction: "http://tempuri.org/Add"
<?xml version="l.0" encoding="utf-8"?>
<soap:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<Add xmlns="http://tempuri.org/">
<x>9</x>
<y>3</y>
</Add>
</soap:Body>
</soap:Envelope>

Затем программа записывает ответ. Ответ, снова-таки, начинается с заголовков протокола передачи гипертекстовых файлов HTTP. За ними следует возвращаемый результат, 12, закодированный в соответствии со спецификацией SOAP.

НТТР/1.1 200 ОК
Server: Microsoft-IIS/5 . О
Date: Tue, 30 Oct 2001 16:49:42 GMT
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Content-Length: 358
<?xml version="l.0" encoding="utf-8"?>
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XM LSchema">
<soap:Body>
<AddResponse xmlns="http://tempuri.org/">
<AddResult>12</AddResult>
</AddResponse>
</soap:Body>
</soap:Envelope>



Особенности форматирования данных согласно спецификации SOAP

Прежде чем завершить предварительное знакомство с протоколом SOAP и языком WSDL, мы более пристально рассмотрим взаимосвязь между протоколом SOAP, языком WSDL и спецификацией XML Schema. Как упоминалось раньше, способ кодирования данных, которые передаются по протоколу SOAP между удаленными приложениями, построенными на основе технологии .NET, отличается от способа кодирования данных, который используется Web-службами и параллельно-последовательным преобразователем (serializer) языка XML.
Чтобы проиллюстрировать отличия между этими двумя способами кодирования данных, передающихся по протоколу SOAP, мы рассмотрим две программы, которые преобразуют один и тот же объект в последовательную форму. Обе программы создают кольцевой, или циклический список, состоящий из двух элементов типа customer (клиент). Первая программа кодирует данные, используя параллельно-последовательный преобразователь (serializer) SOAP платформы .NET. Вторая программа кодирует данные, передающиеся по протоколу SOAP, так, как это делают Web-службы. Программы расположены в каталоге SOAP Differences в подкаталогах Formatter (Форматер) и WebServ-ice соответственно.
Первая программа, Formatter (Форматер), сначала создает циклический список, а затем с помощью SOAP-форматера среды .NET, преобразует его в последовательную форму и сохраняет на диске в файле cust.xml. Чтобы продемонстрировать именно SOAP-сериализацию (и ее отличие от методики, применяемой Web-службами), мы создадим класс Test (Тест), производный от класса WebService. Ниже приведено содержимое файла Formatter.h. Обратите внимание, что класс Customer (Клиент) имеет атрибут Serializ-able (Преобразуемый в последовательную форму).

[Serializable]
// [Преобразуемый в последовательную форму]
public _gc class Customer
// класс сборщика мусора Клиент
{
public:
String *name; // Строка
long id; // идентификатор
Customer *next; // Клиент
};
public _gc class Test : public WebService
// класс сборщика мусора Испытание: WebService
{
public:
static void Main()
{
Test *test = new Test; // Испытание
Customer *list = test->GetList();
FileStream *s =
new FileStream("cust.xml", FileMode::Create);
// Создать файл
SoapFormatter *f = new SoapFormatter;
f->Serialize (s, list);
// Преобразовать в последовательную форму (s, список);
s->Close ();
}
Customer *GetList() // Клиент
{
Customer *custl = new Customer;
// Клиент *custl = новый Клиент;
cust1->name = "John Smith"; // Джон Смит
cust1->id = 1; // идентификатор = 1
Customer *cust2 = new Customer;
// Клиент *cust2 = новый Клиент;
cust2->name = "Mary Smith"; // Мэри Смит
cust2->id =2; // идентификатор = 2
cust2->next = custl; // следующий
custl->next = cust2; // следующий
return custl;
}
};

Данная программа генерирует файл [В приведенном ниже файле для удобства ориентирования вставлены комментарии вида <! — комментарий -->, в самом файле, их, естественно, не будет. — Прим. ред.] cust.xml, который содержит кодированные данные, передающиеся по протоколу SOAP. Обратите внимание, что для идентификации объектов и полей используется атрибут id (идентификатор). Атрибут href используется в качестве объектной ссылки.

<SOAP-ENV:Body>
<al:Customer id="ref-1">
<name id="ref-3">John Smith</name> <!-- Джон Смит -->
<id>1</id> <!— идентификатор —>
<next href="#ref-4"/>
</a1:Customer>
<a1:Customer id="ref-4">
<name id="ref-5">Mary Smith</name> <!-- Мэри Смит -->
<id>2</id> <!-- идентификатор -->
<next href="#ref-1"/>
</a1:Customer>
</SOAP-ENV:Body>

Вторая программа WebService, как можно догадаться из ее названия, является Web-службой. Ниже приведено содержимое файла CustomerList. asmx.

<%@ WebService class="Test" %>

Исходный файл WebService.h содержит следующий код. Обратите внимание, что на этот раз класс Customer (Клиент) не имеет атрибута Serializable (Преобразуемый в последовательную форму).

public _gc class Customer
// класс сборщика мусора Клиент
{
public:
String *name; // Строка
long id; // идентификатор
Customer *next; // Клиент
};
public _gc class Test : public WebService
// класс сборщика мусора Испытание: WebService
{
public:
[WebMethod]
Customer *GetList() // Клиент
{
Customer *custl = new Customer;
// Клиент *custl = новый Клиент;
custl->name = "John Smith"; // Джон Смит
custl->id = 1; // идентификатор = 1
Customer *cust2 = new Customer;
// Клиент *cust2 = новый Клиент;
cust2->name = "Mary Smith"; // Мэри Смит
cust2->id =2; // идентификатор = 2
cust2->next = custl; // следующий
cust1->next = cust2; // следующий
return cust1;
}
};

Чтобы запустить данную профамму, создайте на основе каталога SOAP Differ-ences\WebService виртуальный каталог SOAPWebServiceTest. Затем укажите в адресной строке Internet Explorer следующий унифицированный указатель информационного ресурса (URL) http://localhost/SOAPWebServiceTest/CustomerList.asmx?op=GetList. Internet Explorer распознает, что файл, определяемый этим указателем информационного ресурса (URL), реализует Web-службу. Вид окна Internet Explorer приведен на рис. 11.3.
Если вы попытаетесь активизировать Web-службу прямо сейчас, то будет выдано следующее сообщение об ошибке:

System.Exception: There was an error generating the XML
document. ---> System.Exception: A circular reference
was detected while serializing an object of type Customer.
at System.Xml.Serialization.XmlSerializationWriter.
WriteStartElement(String name, String ns, Object o,
Boolean writePrefixed)
...
at System.Xml.Serialization.XmlSerializer.Serialize
(XmlWriter xmlWriter, Object o,
XmlSerializerNamespaces namespaces)
...

Пере вод такой [Добавлен редактором русского перевода. — Прим. ред.]:

Система.Исключение: Была ошибка при генерации XML-
документа.---> Система.Исключение: циклическая ссылка
была обнаружена при преобразовании в последовательную
форму объекта типа
Клиент.
в Системе.Xml.Преобразование в последовательную
форму XmlSerializationWriter.
WriteStartElement (Строковое имя, Строка ns, Объект о,
Булева переменная wrxtePrefixed)
...
в Системе.Xml.Преобразование в последовательную
форму.XmlSerializer.Преобразовать в последовательную форму
(XmlWriter xmlWriter, Объект о,
XmlSerializerNamespaces пространство имен)
...



Рис 11.3 Internet Explorer распознал файл CustomerList. автхкак Web-службу

Причина ошибки в том, что параллельно-последовательный преобразователь языка XML, который используется Web-службой, не умеет обрабатывать циклические ссылки Если в тексте закомментировать строку cust2 .next = custl, а затем снова скомпоновать проект и скопировать сборку WebService. dll в каталог bin, Web-служба возвратит следующий ответ [В приведенном ниже файле для удобства ориентирования вставлены комментарии вида < ' -- комментарий -->, в самом файле, их, естественно, не будет — Прим ред].
<name>John Smith</name> <'—имя — Джон Смит -->
<id>l</id> <'— идентификатор —>
<next>
<name>Mary Smith</name> <'—имя — Мэри Смит —> <id>2</id> <'— идентификатор —> <next xsi:nil="true" />
</next>
В этом случае, в отличие от случая с распределенными приложениями, реальная взаимосвязь между объектами не имеет никакого значения Но как объяснить то, что протокол SOAP, посредством которого обмениваются данными удаленные приложения, построенные на платформе NET, может передавать отношения между объектами, а протокол SOAP, используемый Web-службами, этого делать не умеет9
Протокол SOAP обрабатывает сложные отношения (множественное наследование, графы и тому подобное), существующие в объектной модели Схема XML (XML Schema) отражает наследование обрабатываемого XML-документа Документ при этом можно представить в виде дерева с единственным корнем В этом дереве каждый узел имеет одного родителя Так как протокол SOAP разрабатывался тогда, когда Схема XML (XML Schema) еще не была завершена, в него вошли некоторые расширения, позволяющие обрабатывать подобные случаи Эти расширения описаны в разделе 5 (Section 5) спецификации SOAP Поэтому их часто называют правилами кодирования раздела 5 (Section 5 encoding rules)
Именно в правилах кодирования раздела 5 сказано, что расширения протокола SOAP нельзя использовать, если кодируемый документ будет проверяться на соответствие схеме Следовательно, NET-XML-классы, выполняющие сериализацию (преобразование в последовательную форму), эти расширения и не используют С другой стороны, параллельно-последовательный преобразователь, который обрабатывает удаленные объекты, развернутые на платформе NET, не производит проверку правильности схемы обрабатываемого документа Для него первостепенную роль играет точность преобразования удаленных объектов Поэтому параллельно-последовательный преобразователь использует все правила раздела 5 Чтобы максимально расширить функциональную совместимость (способность взаимодействия), в реализации Web-служб стремятся использовать формы, совместимые со Схемой XML (XML Schema), то есть, формы, которые можно проверить на соответствие схеме [Читатели, знакомые с моделью компонентных объектов Microsoft (COM), могут попытаться вспомнить, как заместитель обрабатывает совпадение имен указателей в том случае, когда атрибут pomter_default(unique) не используется.]. Конечно, можно привести контраргумент, что в том случае, когда XML-код генерирует машина, проверка правильности схемы не так уж и важна Но в промышленности такой подход пока еще не используется [Здесь мы не будем детально обсуждать, а лишь упомянем о том, что существуют атрибуты, с помощью которых вы можете включить в класс вашей Web-службы методы, использующие правила кодирования раздела 5].
Чтобы приложения и Web-службы, построенные на платформе различных операционных систем, могли взаимодействовать, сначала опишите Web-службы с помощью Схемы XML (Schema XML), и лишь затем опишите их с помощью языка WSDL. Запустив сервисную программу Wsdl с ключом /server, вы можете создать абстрактный класс, который может быть использован в качестве основы для файла . asmx. Если же начать с объектной модели, а затем описать полученную модель с помощью Схемы XML (XML Schema), то полученные системы, скорее всего, будут несовместимы. Конечно, когда используются лишь простые типы и структуры данных, никаких проблем возникнуть не должно. Но если у вас уже имеются объектные модели, тогда вам может понадобиться дополнительный слой (упаковщик), который интерпретирует то, что происходит в слое Web-служб, в терминах объектной модели. Иными словами, такой упаковщик переводит (транслирует) обращения Web-служб на язык объектной модели. Добиться нормальной совместной работы объектных моделей, построенных на разных платформах, — вот наиболее трудная техническая задача в технологии Web-служб [Рассматривать здесь вопросы безопасности Web-служб мы не планируем Отметим лишь, что если не удается построить даже объектные модели, работа которых подчинялась бы некоторым правилам, то говорить о безопасности уже не приходится.].



Класс WebService

Как мы показали раньше, Web-служба представляет собой не что иное, как обработчик запросов, поступающих по протоколу передачи гипертекстовых файлов HTTP. В качестве такового Web-служба имеет доступ к внутренним объектам, которые содержатся в этих запросах. Внутренние объекты запроса, передающегося по протоколу передачи гипертекстовых файлов HTTP, обсуждались в разделе "Состояния в приложениях ASP.NET" главы 10 "ASP.NET и Web-формы". Доступ к этим внутренним объектам обеспечивают свойства, которые содержатся в классе WebService.
Класс Web-службы не обязательно должен быть производным от класса WebService, который входит в состав каркаса. При необходимости класс Web-службы может быть производным от любого базового класса. Тогда получить доступ к внутренним объектам можно с помощью рассматриваемого объекта HttpContext. Класс WebService является производным от класса MarshalByRefObject. Поэтому, если класс вашей Web-службы является производным от другого базового класса и вы хотите, чтобы служба об-" рабатывала удаленные запросы, тогда убедитесь, что ее класс является производным от класса MarshalByRef Object. Информацию о запросе, который передается по протоколу передачи гипертекстовых файлов HTTP, можно получить с помощью объекта HttpContext. Информацию о текущем запросе можно получить при помощи статического свойства Current (Текущий).



Использование шаблона Managed C++ web Service (Web-службы на управляемом C++)

Управляемую Web-службу можно достаточно легко написать на языке C++ с управляемыми расширениями Используя шаблон Managed C++ Web Service Template (Web-служба на управляемом C++), создайте проект пусковой системы. Следуйте приведенным ниже пошаговым инструкциям Создаваемый проект содержится в виде уже готового решения в каталоге ManagedWebService. Чтобы самостоятельно создать проект, который будет расположен в каталоге Demos (Демонстрации), выполните следующие шаги:

Из меню File (Файл) выберите команду New (Создать). Затем выберите команду Project (Проект). В окне Project Types (Типы проектов) щелкните на папке Visual C++ Projects (Проекты на Visual C++). В окне Templates (Шаблоны) выделите пиктограмму Managed C++ Web Service (Web-служба на управляемом C++). Введите ManagedWebService в качестве названия проекта. В поле Location (Расположение) установите значение C:\OI\NetCpp\Chapll\ Demos. Диалоговое окно New Project (Новый проект) с заполненными полями приведено на рис. 11.4.



Рис. 11.4 Диалоговое окно New Project (Новый проект) в котором выбран шаблон Managed C++ Web Service (Web-служба на управляемом C++)

    Щелкните на ОК. Сгенерированный код мы просмотрим позже. Сейчас же мы создадим проект и запустим его. Для этого раскройте меню Build (Скомпоновать) и выберите команду Build (Скомпоновать). Можно также воспользоваться комбинацией клавиш Ctrl+Shift+B. Затем запустите проект. Режим отладки мы использовать не будем, поэтому из меню Debug (Отладка) выберите команду Start Without Debugging (Запустить без отладки). Появится окно броузера, показанное на рис. 11.5.


Код, генерируемый шаблоном Managed C++ Web Service (Web-служба на управляемом C++)

Шаблон Managed C++ Web Service (Web-служба на управляемом C++) создает код стартовой системы для проекта Web-службы. На рис. 11.6 приведено окно Solution Explorer (Поиск решения), в котором открыт проект ManagedWebService.
Файл ManagedWebService. asmx содержит следующую строку

<%@ WebService Class= ManagedWebService.Classl %>

Кроме этого файла в состав проекта еще входит исходный файл ManagedWebService. срр и связанный с ним заголовочный файл ManagedWebService.h. Обратите внимание, что файл ManagedWebService. h содержит директиву #using, которая ссылается на сборку System. Web. Service. dll. В этой сборке содержится важная информация о типах, используемых Web-службой Кроме того, можно видеть, что Web-служба реализована с помощью класса Classl, который принадлежит пространству имен ManagedWebService. Данный класс содержит метод HelloWorld, который имеет атрибут [System: :Web: : Services: :WebMethod] ([Система-Сеть-.Службы-WebMethod]). Этот атрибут необходим для того, чтобы Web-клиент мог вызвать данный метод как метод Web-службы. Код, который содержится в исходном файле ManagedWebService . срр приведен ниже.



Рис 11.5. Вид, который имеет Web-служба, построенная с помощью шаблона Managed C++ Web Service (Web-служба на управляемом C++)

#mclude "stdafx.h"
tinclude " ManagedWebService.h"
#include "Global.asax.h"
namespace ManagedWebService
// пространство имен ManagedWebService
{
// ПРИМЕР WEВ-СЛУЖБЫ
// Пример службы HelloWorld () возвращает...
// Чтобы проверить эту Web-службу, убедитесь, что .asmx...
// в свойствах проекта
// установлен в качестве отладочного
// унифицированного указателя информационного ресурса (URL)
// для протокола передачи гипертекстовых файлов HTTP.
// и нажмите F5.
String _go* Classl::HelloWorld()
{
// TODO: Add the implementation of your ...
// TODO: Добавьте реализацию вашего...
return S"Hello World!";
// Привет, мир! }
};



Рис 11.6 Окно Solution Explorer (Поиск решения), в котором открыт проект Web-службы на управляемом C++, созданный на основе шаблона Managed C++ Web Service (Web-служба на управляемом C++)

Теперь приведем содержимое исходного файла ManagedWebService .h

// ManagedWebService .h
#pragma once
#using <System.Web.Services.dll>
using namespace System;
// использование пространства имен Система;
using namespace System::Web;
// использование пространства имен Система::Сеть;
using namespace System::Web::Services;
// использование пространства имен Система::Сеть::Службы;
namespace ManagedWebServiee
// пространство имен ManagedWebServiee
{
public _gc // сборщик мусора
class Classl : public WebService // класс Classl: WebService
{
public:
// ПРИМЕР WEB-СЛУЖБЫ СЕТИ
// Пример службы HelloWorld() возвращает...
// Чтобы проверить эту Web-службу, убедитесь, что...
// установлен для проекта в качестве отладочного
// унифицированного указателя информационного ресурса (URL)
// для протокола передачи гипертекстовых файлов HTTP..
// и нажмите F5.
[System::Web::Services::WebMethod]
// [Система::Сеть::Службы::WebMethod]
String _gc* HelloWorld();
// TODO: Add the methods of your Web Service here
// TODO: Добавьте здесь методы вашей Web-службы
};
}

Кроме рассмотренных выше файлов, были также созданы следующие файлы: Global.азах, ManagedWebServiee.vsdisco и Web.config. Файл обнаружения ManagedWebServiee . vsdisco — это файл в формате XML. Он был создан мастером, и используется клиентами при поиске Web-службы.
Чтобы просмотреть Web-службу и увидеть, как работает метод HelloWorld, можно воспользоваться стандартным броузером, как и в предыдущем примере. При этом мы со-. всем не изменили исходный проект ManagedWebServiee, созданный с помощью шаблона Managed C++ Web Service (Web-служба на управляемом C++). В следующем разделе мы рассмотрим проект Arithmetic (Арифметика), который был создан аналогично проекту ManagedWebServiee. А вот код проекта Arithmetic (Арифметика), сгенерированный с помощью шаблона Managed C++ Web Service (Web-служба на управляемом C++), был сильно изменен, прежде чем он приобрел окончательный вид.



Арифметическая Служба Сети, или Web-служба Arithmetic (Арифметика)

В данном разделе главы будет рассмотрена Web-служба Arithmetic (Арифметика), созданная на основе шаблона Managed C++ Web Service (Web-служба на управляемом C++). Мы научимся работать с внутренними объектами Web-службы. Но прежде чем двигаться дальше, посмотрите, как выглядит Web-служба в окне вашего броузера (рис. 11.7) Из окна броузера можно запустить каждый метод Web-службы и убедиться, что методы работают корректно.



Рис. 11. 7. Вид завершенной Web-службы, созданной с помощью шаблона Managed C++ Web Service (Web-служба на управляемом C++)



Использование внутренних объектов

Данная Web-служба имеет несколько методов, на примере которых мы проиллюстрируем, как используются внутренние объекты. Дальше мы увидим, что эти внутренние объекты аналогичны внутренним объектам в ASP.NET Из используемых методов те два, которые подсчитывают общую сумму чисел, иллюстрируют использование состояния приложения и состояния сеанса.
В соответствующих обработчиках событий, которые содержатся в файле global.asax.h, значение суммы инициализируется нулем. Для Web-служб файл Global. asax. h играет ту же роль, что и для ASP.NET. Назначение этого файла подробно обсуждалось в главе 10 "ASP.NET и Web-формы". Так как класс Glooal является производным от класса System: :Web: :HttpApplication (Система "Сеть" HttpApplication), то он может получить доступ ко внутренним объектам Application (Приложение) и Session (Сеанс).

public _gc class Global :
// класс сборщика мусора Глобальный:
public System::Web::HttpApplication
// общедоступная Система::Сеть::HttpApplication
{
protected: // защищенный
void Application_Start(Object *sender, EventArgs *e)
{
Application->set_Item("TotalSum", _box(0.0));
// Приложение
}
void Session_Start(Object *sender, EventArgs *e)
{
Session->set_Item("SessionSum", _box(O.O)); // Сеанс
}
...

Присвоив аргументу EnableSession конструктора WebMethod значение true (истина), мы включили состояние сеанса для метода SessionSum. В начале каждого сеанса значению суммы присваивается нуль. С другой стороны, аргумент EnableSession Web-метода CumulativeSum по умолчанию принимает значение false (ложь). Это означает, что общей сумме присваивается исходное значение нуль лишь в том случае, если приложение с данной Web-службой перезапускается. Объект HttpContext использует встроенный объект Application (Приложение), чтобы показать, как используется данный класс.
Если вы посмотрите на код, приведенный ниже, то вам станет ясно, что объекты HttpApplication, WebService и HttpContext указывают на один и тот же внутренний объект. Чтобы сохранить состояние приложения или состояние сеанса, можно воспользоваться коллекциями объектов HttpApplicationState и HttpSessionState.

[System::Web::Services::WebMethod(EnableSession = true)]
// [Система::Сеть::Службы::WebMethod (EnableSession = истина)]
double SessionSum(double x)
{
double d = *dynamic_cast<Double *>
(Session->get_Item("SessionSum")); // Сеанс
Session->set_Item("SessionSum", _box(d + x)); // Сеанс
return *dynamic_cast<Double *>
(Session->get_Item("SessionSum")); // Сеанс
}
[System: :Web: : Services: :WebMethod]
// [Система: :Сеть: -.Службы: :WebMethod]
double CumulativeSum(double x)
{
double sum = *dynamic_cast<Double *> // сумма
(Application->get_Item("TotalSum")); // Приложение
sum = sum + x;
// сумма = сумма + x;
Application->set_Item("TotalSum", _box(sum)); // сумма
return *dynamic_cast<Double *>
(Application->get_It^m("TotalSum")); // Приложение
}
...

Метод GetUserAgent иллюстрирует, как с помощью объекта Context (Контекст) извлечь информацию о запросе. Тип используемого клиента возвращается Web-службе. Для извлечения информации из внутреннего объекта Server (Сервер) служит метод

GetServerlnfo.
[System::Web::Services::WebMethod]
// [Система: -.Сеть: :Службы: :WebMethod]
String *GetUserAgent()
{
return Context->Request->UserAgent;
// Koнтeкст->3anpoc->UserAgent;
}
[System::Web::Services::WebMethod]
// [Система::Сеть::Службы::WebMethod]
String *GetServerInfo()
{
String *msg = String::Format(
// Строка *msg = Строка:: Формат (
"Timeout for {0} = {!}; Located at {2}",
// "Время ожидания для {0} = {1}; расположенного в {2} ",
Server->MachineName, // Сервер _box(Server->ScriptTimeout), // Сервер
Server->MapPath(""}); // Сервер
return msg; // сообщение
}

Возможности Web-службы будут продемонстрированы с помощью консольной программы [Вам придется самостоятельно скопировать файл Anthmetic.dll из каталога Anthmetic\Debug в каталог AnthmeticClient\Debug. Если этого не сделать, при попытке запустить файл ArithmeticChent.exe возникнет необрабатываемое исключение (System.lO.FileNotFoundException).] ArithmeticClient. Создать класс-заместитель можно в среде Visual Studio.NET. Для этого в меню Project (Проект) выберите команду Add Web Reference (Добавить Web-ссылку), затем в строке Address (Адрес) введите адрес Web-службы и нажмите клавишу Enter. В данном примере нужно ввести следующий унифицированный указатель информационного ресурса (URL) http: //localhost/Arithmetic/Arithmetic.asmx. В диалоговом окне будет отображена информация о Web-службе Arithmetic (Арифметика), рис. 11.8.
Чтобы добавить Web-ссылку, щелкните на кнопке Add Reference (Добавить ссылку). Таким образом в файле WebService. h будет добавлена ссылка на класс-заместитель.

#using <Arithmetic.dll>

Дальше вычисляется общая сумма чисел, которая хранится во внутреннем объекте Application (Приложение) и внутреннем объекте Session (Сеанс).

Arithmetic *a = new Arithmetic; // Арифметика
double sum; // сумма
for (int i = 0; i < 5; i++)
{
sum = a->CumulativeSum(i); // сумма
Console::WriteLine(
"Adding {0}, Application sum is now {!}",
// "Добавляем {0}, Сумма Приложения теперь {1} ",
_box(i), _box(sum)); // сумма
}
double sessionSum;
for (int i = 0; i < 5; i++)
{
sessionSum = a->SessionSum(i);
Console.:WriteLine(
"Adding {0}, Session sum is now {!}",
// "Добавляем {0}, Сумма Сеанса теперь {1} ",
_box(i), _box(sessionSum));
}



Рис 11.8 Visual Studio NET отображает информацию о Web-службе Arithmetic (Арифметика)

В результате будут отображены данные, которые приведены ниже Конкретное значение суммы приложения зависит от того, сколько раз приложение было запущено

Adding 0, Application sum is now 0
Adding 1, Application sum is now 1
Adding 2, Application sum is now 3
Adding 3, Application sum is now 6
Adding 4, Application sum is now 10
Adding 0, Session sum is now 0
Adding 1, Session sum is now 1
Adding 2, Session sum is now 2
Adding 3, Session sum is now 3
Adding 4, Session sum is now 4

Перевод такой [Добавюн редактором русского перевода — Прим ред].

Добавляем 0, Сумма Приложения теперь 0
Добавляем 1, Сумма Приложения теперь 1
Добавляем 2, Сумма Приложения теперь 3
Добавляем 3, Сумма Приложения теперь 6
Добавляем 4, Сумма Приложения теперь 10
Добавляем 0, Сумма Сеанса теперь 0
Добавляем 1, Сумма Сеанса теперь 1
Добавляем 2, Сумма Сеанса теперь 2
Добавляем 3, Сумма Сеанса теперь 3
Добавляем 4, Сумма Сеанса теперь 4

Теперь мысоздадим еще один экземпляр класса-заместителя и вызовем тот же метод

Arithmetic *a2 = new Arithmetic; // Арифметика
for (int i=0; i < 5; i++)
{
sum = a2->CumulativeSum(i) ; // сумма
Console :WriteLine(
"A(ding {0}, Application sum is now {1}",
//"Добавляем {0}, Сумма Приложения теперь {1} ",
_box(i), _box(sum)); // сумма
}
for (int 1=0; i < 5; i++)
sum = a2->:essionSum(i); // сумма
Console: :WriteLine(
"Adding {0}, Session sum is now {!}",
// "До(авляем {0}, Сумма Сеанса теперь {1} ",
_box(i), _box(sum)); // сумма
}

В ходе выюлнения этой программы будут отображены приведенные ниже результаты Обратите внимание, что сумма приложения продолжает увеличиваться дальше, в то время как суше сеанса сначала присваивается значение ноль и лишь затем она начинает увеличиваться. Открыть новое окно броузера — это не единственный способ начать новый сеанс досупа к Web-службе.

Adding 0, Application sum is now 10
Adding 1, Application sum is now 11
Adding 2, Application sum is now 13
Adding 3, Application sum is now 16
Adding 4, Application sum is now 20
Adding 0, Session sum is now 0
Adding 1, Session sum is now 1
Adding 2, Session sum is now 2
Adding 3, Session sum is now 3
Adding 4, Session sum is now 4

Перевод тасой

Добавляем 0, Сумма Приложения теперь 10
Добавляем 1, Сумма Приложения теперь 11
Добавляем 2, Сумма Приложения теперь 13
Добавляем 3, Сумма Приложения теперь 16
Добавляем 4, Сумма Приложения теперь 20
Добавляем 0, Сумма Сеанса теперь О
Добавляем 1, Сумма Сеанса теперь 1
Добавляем 2, Сумма Сеанса теперь 2
Добавляем 3, Сумма Сеанса теперь 3
Добавляем 4, Сумма Сеанса теперь 4

В заключеше, мы вызываем Web-методы GetUserAgent и GetServerlnfо

Console.:WriteLine(a2->GetUserAgent());
Console:.WriteLine(a2->GetServer!nfо() ) ;

В результате будут выведены данные, которые выглядят примерно следующим образом

Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client
Protocol 1.0.2914.16)
Timeout for HPDESKTOP = 90; Located at c:\inetpub\wwwroot\Arithmetic



Web-служба Hotel Broker (Брокер гостиницы)

Пришло время сделать следующий шаг на пути изучения конкретного примера Теперь на основе компонентов Customer (Клиент) и Hotel (Гостиница) Web-службы Hotel Broker (Брокер гостиницы) мы создадим самостоятельные Web-службы Основная Web-служба находится в подкаталоге HotelBroker-WebService данной главы Клиенты Acme будут ее использовать для того, чтобы забронировать место в гостинице Она также будет использоваться в административных целях, связанных с Hotel Broker (Брокер гостиницы) Обсуждаемые Web-службы расположены по следующим адресам

http //localhost/CustomerWebService/CustomerWebService asmx http //localhost/HotelWebService/HotelWebService asmx

На рис 11.9 и 11.10 показаны окна броузера, в которых содержится информация о Web-службах CustomerWebService и HotelWebService соответственно
Сами классы-заместители содержатся в сборке proxy.dll Два командных файла, с помощью которых можно создать классы-заместители и построить данную сборку, находятся в подкаталоге WebServiceProxies, который относится к этому примеру



Рис 11.9 Информация о Web-службе CustomerWebService



Рис 11.10 Информация о Web-службе HotelWebService



Рис 11.11 AcmeGui обращается к Web-службе

В подкаталоге AcmeWeb2, относящемся к рассматриваемому конкретному примеру, находится версия программы AcmeLib, которая вместо сборок Customer (Клиент) и Hotel (Гостиница) использует сборку proxies (заместители). Все ссылки на компоненты Customer (Клиент) и Hotel (Гостиница), содержавшиеся на Web-странице системы бронирования Acme, а также программы, расположенные в подкаталоге HotelBro-kerAdministration, были удалены. На рис. 11.12 показано как выглядит Web-служба в окне Internet Explorer.



Web-служба Customer (Клиент)

Чтобы создать Web-службу Customer (Клиент), мы создадим с помощью шаблона Managed C++ Web Service (Web-служба на управляемом C++) проект Web-службы и назовем его CustomerWebService. Этот проект будет использован компонентом Customer (Клиент) для реализации деталей Web-службы. Ниже приведено содержимое файла CustomerWebService . asmx, который входит в состав проекта.

<%@ WebService class = "CustomerWebService.CustomerWebService" %>

А вот и код, реализующий класс CustomerWebService:



Рис. 11.12. AcmeWeb2 получает доступ к Web-службе

public _gc class CustomerWebService :
public WebService
// класс сборщика мусора CustomerWebService: WebService
{
private: // частный
Customers * customers; // Клиенты public:
CustomerWebService ()
{
customers = new Customers("HotelBroker");
// клиенты = новые Клиенты ("HotelBroker");
}
[WebMethod]
int RegisterCustomer(
String *firstName, // Строка
String *lastName, // Строка
String *emailAddress) // Строка
{
int customerId;
customerld = customers->RegisterCustomer( // клиенты
firstName, lastName, emailAddress) ;
return customerld;
}
[WebMethod]
void UnregisterCustomer(int customerId)
{
customers->UnregisterCustomer(customerld); // клиенты }
[WebMethod]
[Xmllnclude(_typeof(CustomerListltem))]
ArrayList *GetCustomer(int customerld)
{
ArrayList *ar;
ar = customers->GetCustomer(customerld) ; // клиенты
return ar;
}
[WebMethod]
void ChangeEmailAddress(
int customerld, String *emailAddress)
{
customers->ChangeEmailAddress( // клиенты
customerld, emailAddress);
}
};

В коде содержится единственный незнакомый нам атрибут Xmllnclude. Он указывает, что для преобразования определенного пользователем типа данных, в нашем случае CustomerListltem, XmlSerializer обычно может создать протокол SOAP. Этот атрибут находится в пространстве имен System: :Xml: : Serialization (Система::Xml:: Преобразование в последовательную форму). Тем не менее, если вы посмотрите на класс-заместитель данной Web-службы, который расположен в каталоге WebService-Proxies, вы увидите, что заместитель GetCustomer (customerproxy.cs) возвращает только массив объектов.

public object[] GetCustomer(int customerld)
// общедоступный объект[] GetCustomer(int customerld)

Данный атрибут указывает, что параллельно-последовательный преобразователь должен сохранить тип данных, определенный пользователем. Однако по протоколу SOAP могут передаваться лишь универсальные типы объектов. Поэтому код программы AcmeLib (содержится в файле Acme. cs) должен обрабатывать возвращаемый тип как объект, а затем извлечь из него тип, определенный пользователем.

object[] al = customers.GetCustomer(hotelCustomerld);
foreach(CustomerListltem cust in al)
{
currentUser.HotelCustomerld = hotelCustomerld;
currentUser.FirstName = cust.FirstName;
currentUser.LastName = cust.LastName;
currentUser.EmailAddress = cust.EmailAddress;
}

Все другие объекты компонентов Customer (Клиент) и Hotel (Гостиница) рассматриваемой Web-службы, которые содержатся в списке ArrayList, трактуются как массивы объектов, из которых нужно извлечь требуемый тип. Те массивы, которые используют такие типы данных, как строки и целые числа, обрабатывать с помощью XmlSerial-izer нет необходимости.



Web-служба Hotel Broker (Брокер гостиницы)

В случае Web-службы Hotel Broker (Брокер гостиницы), сборка Hotel (Гостиница) была модифицирована таким образом, что теперь она сама играет роль Web-службы. В файле HotelWebService . asmx должна присутствовать ссылка только на класс Hotel-Broker, который реализован в сборке Hotel (Гостиница).

<%@ WebService class = "01.NetCpp.Acme.HotelBrokerWebService, Hotel" %>

В данном примере код ничем не отличается от кода предыдущей версии компонента, за одним исключением. В коде дополнительно присутствуют атрибуты, которые указывают, что его нужно преобразовать в Web-службу. Имена Web-служб должны быть уникальными. Поэтому, чтобы присвоить уникальное имя одному из перегружаемых методов GetHotels, следует использовать свойство MessageName атрибута WebMethod. Ниже приведен код, который содержится в файле Hotel.h, расположенном в каталоге CaseStudy\HotelBrokerWebServiсе.

[WebMethod(MessageName="GetAllHotels")]
[Xmllnclude(_typeof(HotelListltem))]
ArrayList *GetHotels()
{
...
}



Соображения по поводу проектирования

Главный фактор, который влияет на производительность системы — это время ожидания при передаче по сети. Отсюда следует, что число запросов, передаваемых Web-службе или базе данных по сети, должно быть минимальным. В случае Web-службы Но-telBroker информация о резервировании для клиента хранится в наборе данных, который используется в качестве кэша, так что запрос к базе данных понадобится лишь в случае модификации базы данных. Та же ситуация реализуется и при отслеживании гостиниц и городов. Но есть и некоторые отличия, поскольку администратор может добавить новую гостиницу. Маловероятно, что это произойдет именно в тот непродолжительный промежуток времени, на протяжении которого выполняется резервирование для клиента. Конечно, такие типы данных можно кэшировать внутри самой Web-формы. А тогда вызывать Web-службу нет необходимости.
Протокол передачи гипертекстовых файлов HTTP не сохраняет информацию о состоянии сеанса (и приложения). Следовательно, не сохраняет эту информацию и протокол SOAP. Чтобы ваши приложения и Web-службы более просто масштабировались, хранимая информация о состоянии должна быть минимальной. Ведь именно в этом случае объекты (например сеансы с базой данных) можно легче объединить в пул и использовать повторно, — так что необходим меньший объем памяти, вследствие чего доступно больше ресурсов, которые могут обрабатывать большее количество запросов. Это означает, что Web-службы выступают уже не в роли полноценных объектов, а как конечные точки передачи данных. Рассматривая конкретный пример, мы еще этого не ощутили. И не удивительно, поскольку нам нужно было проиллюстрировать использование определенных технологий и способы правильного распределения функциональных возможностей, а это зависит от особенностей используемого приложения и времени ожидания при передаче по сети.
Чтобы избежать непроизводительных издержек, связанных с сетью, для кэширования информации можно использовать свойство Cache Duration Web-метода или свойство Cache класса HttpContext.



Резюме

Web-службы предоставляют средства, расширяющие функциональные, возможности компонентов за счет предоставления к ним доступа из любого места сети, объединяющей различные платформы и языки программирования, разработанные различными производителями. Однако в отличие от удаленной обработки данных в среде .NET, количество типов данных, используемых Web-службами, значительно меньше.
Тем не менее, если при проектировании приложения с самого начала учитывать Схему XML (XML Schema), а затем создать свой язык WSDL и классы Web-служб, то вероятность того, что созданное приложение сможет взаимодействовать с другими, значительно возрастает.